package com.ithuameng.admin.security;

import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap;
import com.ithuameng.admin.config.AdminCfgProp;
import com.ithuameng.admin.constants.CommonConstants;
import com.ithuameng.admin.exception.UnAnuthorizedException;
import io.jsonwebtoken.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Base64;
import java.util.Date;
import java.util.Map;

@Slf4j
@RequiredArgsConstructor
@Component
public class JwtTokenProvider {

	private String secretKey;

	/**
	 * 2小时内有效
	 */
	public static final long TOKEN_VALID_MILLISECONDS = 1000L * 60 * 60 * 2;

	@Resource
	private AdminCfgProp cfgProp;

	@Resource
	@Qualifier("adminUserDetailService")
	private UserDetailsService adminUserDetailService;

	@PostConstruct
	protected void init() {
		secretKey = Base64.getEncoder().encodeToString(cfgProp.getJwt().getSecret().getBytes());
	}

	public String createToken(Map<String, String> attributes) {
		Claims claims = Jwts.claims();
		claims.putAll(attributes);
		Date now = new Date();
		return Jwts.builder().setClaims(claims).setIssuedAt(now)
				.setExpiration(new Date(now.getTime() + TOKEN_VALID_MILLISECONDS))
				.signWith(SignatureAlgorithm.HS256, secretKey).compact();
	}

	public Authentication getAdminAuthentication(String token) {
		Map<String, String> attributes = verify(token);
		UserDetails userDetails = adminUserDetailService.loadUserByUsername(attributes.get(CommonConstants.USER_NAME));
		if (userDetails == null) {
			throw new UnAnuthorizedException("用户不存在");
		}
		return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
	}

	public String getUserPk(String token) {
		return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
	}

	public String resolveToken(HttpServletRequest req) {
		return req.getHeader("Authorization");
	}

	public Map<String, String> verify(final String token) {
		final JwtParser parser = Jwts.parser().setSigningKey(secretKey);
		return parseClaims(() -> parser.parseClaimsJws(token).getBody());
	}

	public boolean validateToken(String jwtToken) {
		try {
			log.debug("[JwtTokenProvider >> validateToken]");
			Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwtToken);
			return !claims.getBody().getExpiration().before(new Date());
		} catch (SignatureException e) {
			log.error("Invalid JWT signature", e);
			return false;
		} catch (MalformedJwtException e) {
			log.error("Invalid JWT token", e);
			return false;
		} catch (ExpiredJwtException e) {
			log.error("Expired JWT token", e);
			return false;
		} catch (UnsupportedJwtException e) {
			log.error("Unsupported JWT token", e);
			return false;
		} catch (IllegalArgumentException e) {
			log.error("JWT claims string is empty.", e);
			return false;
		} catch (Exception e) {
			return false;
		}
	}

	private static Map<String, String> parseClaims(final Supplier<Claims> toClaims) {
		try {
			final Claims claims = toClaims.get();
			final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
			for (final Map.Entry<String, Object> e : claims.entrySet()) {
				builder.put(e.getKey(), String.valueOf(e.getValue()));
			}
			return builder.build();
		} catch (final IllegalArgumentException | JwtException e) {
			return ImmutableMap.of();
		}
	}
}
